package com.acme.logging;

import org.slf4j.Logger;
import org.slf4j.Marker;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * Created by Matveev Alexander on 14.12.13.
 */
class MyLogger implements Logger {

    public enum Level {
        DEBUG(" [DEBUG] "),INFO(" [INFO ] "),ERROR(" [ERROR] "),MUTE("");

        private String mark;

        private Level(String mark) {
            this.mark = mark;
        }

        @Override
        public String toString() {
            return mark;
        }
    }

    private final String name;
    private final BlockingQueue<String> logsToLog = new LinkedBlockingQueue<String>();

    private final LogWriter writer;
    private final LogFormatter formatter;

    private Level logLevel;

    public MyLogger(String name, LogWriter writer, LogFormatter formatter, Level logLevel) {
        this.name = name;
        this.writer = writer;
        this.formatter = formatter;
        this.logLevel = logLevel;

        final Thread thread = new Thread(runnable, this.name + " thread");
        thread.setDaemon(true);
        thread.start();

        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread.interrupt();
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    MyLogger.this.writer.close();
                }
            }
        }));
    }

    private void addLog(Level level, String log) {
        logsToLog.add(formatter.format(getName(), level, log));
    }

    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            while (!Thread.currentThread().isInterrupted()) {
                processQueue();
            }
        }
    };

    private void processQueue() {
        while (!logsToLog.isEmpty()) {
            String nextLog = logsToLog.poll();
            if (nextLog != null) {
                writer.writeLog(nextLog);
            }
        }
    }

    private String replacePlaceholders(String s, Object o) {
        return s.replaceFirst("\\{\\}", o.toString());
    }

    private String replacePlaceholders(String s, Object o, Object o2) {
        return s.replaceFirst("\\{\\}", o.toString()).replaceFirst("\\{\\}", o2.toString());
    }

    private String replacePlaceholders(String s, Object... objects) {
        String string = s;
        for (Object obj : objects) {
            string = string.replaceFirst("\\{\\}", obj.toString());
        }
        return string;
    }


    @Override
    public String getName() {
        return name;
    }

    @Override
    public boolean isTraceEnabled() {
        throw new UnsupportedOperationException();
    }

    @Override
    public void trace(String s) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void trace(String s, Object o) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void trace(String s, Object o, Object o2) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void trace(String s, Object... objects) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void trace(String s, Throwable throwable) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isTraceEnabled(Marker marker) {
        return false;
    }

    @Override
    public void trace(Marker marker, String s) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void trace(Marker marker, String s, Object o) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void trace(Marker marker, String s, Object o, Object o2) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void trace(Marker marker, String s, Object... objects) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void trace(Marker marker, String s, Throwable throwable) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isDebugEnabled() {
        return logLevel.ordinal() <= Level.DEBUG.ordinal();
    }

    @Override
    public void debug(String s) {
        if (!isDebugEnabled()) return;
        addLog(Level.DEBUG, s);
    }

    @Override
    public void debug(String s, Object o) {
        if (!isDebugEnabled()) return;
        debug(replacePlaceholders(s, o));
    }

    @Override
    public void debug(String s, Object o, Object o2) {
        if (!isDebugEnabled()) return;
        debug(replacePlaceholders(s, o, o2));
    }

    @Override
    public void debug(String s, Object... objects) {
        if (!isDebugEnabled()) return;
        debug(replacePlaceholders(s, objects));
    }

    @Override
    public void debug(String s, Throwable throwable) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isDebugEnabled(Marker marker) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void debug(Marker marker, String s) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void debug(Marker marker, String s, Object o) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void debug(Marker marker, String s, Object o, Object o2) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void debug(Marker marker, String s, Object... objects) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void debug(Marker marker, String s, Throwable throwable) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isInfoEnabled() {
        return logLevel.ordinal() <= Level.INFO.ordinal();
    }

    @Override
    public void info(String s) {
        if (!isInfoEnabled()) return;
        addLog(Level.INFO, s);
    }

    @Override
    public void info(String s, Object o) {
        if (!isInfoEnabled()) return;
        info(replacePlaceholders(s, o));
    }

    @Override
    public void info(String s, Object o, Object o2) {
        if (!isInfoEnabled()) return;
        info(replacePlaceholders(s, o, o2));
    }

    @Override
    public void info(String s, Object... objects) {
        if (!isInfoEnabled()) return;
        info(replacePlaceholders(s, objects));
    }

    @Override
    public void info(String s, Throwable throwable) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isInfoEnabled(Marker marker) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void info(Marker marker, String s) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void info(Marker marker, String s, Object o) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void info(Marker marker, String s, Object o, Object o2) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void info(Marker marker, String s, Object... objects) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void info(Marker marker, String s, Throwable throwable) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isWarnEnabled() {
        throw new UnsupportedOperationException();
    }

    @Override
    public void warn(String s) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void warn(String s, Object o) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void warn(String s, Object... objects) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void warn(String s, Object o, Object o2) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void warn(String s, Throwable throwable) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isWarnEnabled(Marker marker) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void warn(Marker marker, String s) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void warn(Marker marker, String s, Object o) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void warn(Marker marker, String s, Object o, Object o2) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void warn(Marker marker, String s, Object... objects) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void warn(Marker marker, String s, Throwable throwable) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isErrorEnabled() {
        return logLevel.ordinal() <= Level.ERROR.ordinal();
    }

    @Override
    public void error(String s) {
        if (!isErrorEnabled()) return;
        addLog(Level.ERROR, s);
    }

    @Override
    public void error(String s, Object o) {
        if (!isErrorEnabled()) return;
        error(replacePlaceholders(s, o));
    }

    @Override
    public void error(String s, Object o, Object o2) {
        if (!isErrorEnabled()) return;
        error(replacePlaceholders(s, o, o2));
    }

    @Override
    public void error(String s, Object... objects) {
        if (!isErrorEnabled()) return;
        error(replacePlaceholders(s, objects));
    }

    @Override
    public void error(String s, Throwable throwable) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isErrorEnabled(Marker marker) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void error(Marker marker, String s) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void error(Marker marker, String s, Object o) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void error(Marker marker, String s, Object o, Object o2) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void error(Marker marker, String s, Object... objects) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void error(Marker marker, String s, Throwable throwable) {
        throw new UnsupportedOperationException();
    }
}
